package com.black.cat.jvm.rtda.heap.methodarea;

import com.black.cat.jvm.classfile.ClassFile;
import com.black.cat.jvm.rtda.JObject;
import com.black.cat.jvm.rtda.heap.ClassLoader;
import com.black.cat.jvm.rtda.heap.constantpool.AccessFlags;
import com.black.cat.jvm.rtda.heap.constantpool.RunTimeConstantPool;

/**
 * @Author blackcat
 * @create 2021/8/14 10:42
 * @version: 1.0
 * @description:Class 相关信息
 */
public class JClass {

    //访问标志
    public int accessFlags;

    //---------------------
    //这些类名都是完全限定名，具有java/lang/Object的形式
    //类名
    public String name;
    //超类
    public String superClassName;
    //接口名
    public String[] interfaceNames;
    //----------------------

    //运行时常量池指针
    public RunTimeConstantPool runTimeConstantPool;

    //字段表
    public JField[] JFields;

    //方法表
    public JMethod[] JMethods;

    //class 加载器
    public ClassLoader loader;

    //超类指针
    public JClass superJClass;

    //接口指针
    public JClass[] interfaces;

    //实例变量占据的空间大小
    public int instanceSlotCount;

    //类变量占据的空间大小
    public int staticSlotCount;

    //静态变量
    public Slots staticVars;

    public boolean initStarted;


    public JClass(ClassFile classFile) {
        this.accessFlags = classFile.getAccessFlag();
        this.name = classFile.className();
        this.superClassName = classFile.superClassName();
        this.interfaceNames = classFile.interfaceNames();
        this.runTimeConstantPool = new RunTimeConstantPool(this, classFile.getConstantPool());
        this.JFields = new JField().newFields(this, classFile.getFieldInfos());
        this.JMethods = new JMethod().newMethods(this, classFile.getMethodInfos());
    }

    public boolean isPublic() {
        return 0 != (this.accessFlags & AccessFlags.ACC_PUBLIC);
    }

    public boolean isFinal() {
        return 0 != (this.accessFlags & AccessFlags.ACC_FINAL);
    }

    public boolean isSuper() {
        return 0 != (this.accessFlags & AccessFlags.ACC_SUPER);
    }

    public boolean isInterface() {
        return 0 != (this.accessFlags & AccessFlags.ACC_INTERFACE);
    }

    public boolean isAbstract() {
        return 0 != (this.accessFlags & AccessFlags.ACC_ABSTRACT);
    }

    public boolean isSynthetic() {
        return 0 != (this.accessFlags & AccessFlags.ACC_SYNTHETIC);
    }

    public boolean isAnnotation() {
        return 0 != (this.accessFlags & AccessFlags.ACC_ANNOTATION);
    }

    public boolean isEnum() {
        return 0 != (this.accessFlags & AccessFlags.ACC_ENUM);
    }


    public String getPackageName() {
        int i = this.name.lastIndexOf("/");
        if (i >= 0) return this.name;
        return "";
    }

    public Slots staticVars() {
        return this.staticVars;
    }

    public RunTimeConstantPool constantPool() {
        return this.runTimeConstantPool;
    }

    public JObject newObject() {
        return new JObject(this);
    }

    public boolean isAssignableFrom(JClass other) {
        if (this == other) return true;
        if (!other.isInterface()) {
            return this.isSubClassOf(other);
        } else {
            return this.isImplements(other);
        }
    }

    public boolean isSubClassOf(JClass other) {
        for (JClass c = this.superJClass; c != null; c = c.superJClass) {
            if (c == other) {
                return true;
            }
        }
        return false;
    }

    public boolean isImplements(JClass other) {

        for (JClass c = this; c != null; c = c.superJClass) {
            for (JClass clazz : c.interfaces) {
                if (clazz == other || clazz.isSubInterfaceOf(other)) {
                    return true;
                }
            }
        }
        return false;

    }

    public boolean isSubInterfaceOf(JClass iface) {
        for (JClass superInterface : this.interfaces) {
            if (superInterface == iface || superInterface.isSubInterfaceOf(iface)) {
                return true;
            }
        }
        return false;
    }

    public JMethod getMainMethod() {
        return this.getStaticMethod("main", "([Ljava/lang/String;)V");
    }

    private JMethod getStaticMethod(String name, String descriptor) {
        for (JMethod method : this.JMethods) {
            if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
                return method;
            }
        }
        return null;
    }

    public JMethod getClinitMethod(){
        return this.getStaticMethod("<clinit>","()V");
    }

    public boolean initStarted(){
        return this.initStarted;
    }

    public void startInit(){
        this.initStarted = true;
    }
}
